/*
 *	PostgreSQL 大对象的管理定义。
 *
 *	contrib/lo/lo.c
 *
 */

#include "postgres.h"

#include "commands/trigger.h"
#include "executor/spi.h"
#include "utils/builtins.h"
#include "utils/rel.h"

PG_MODULE_MAGIC;


/*
 * 这是保护我们免受孤立大对象影响的触发器
 */
PG_FUNCTION_INFO_V1(lo_manage);

Datum lo_manage(PG_FUNCTION_ARGS)
{
	TriggerData *fc_trigdata = (TriggerData *) fcinfo->context;
	int			fc_attnum;			/* 要监控的属性编号	*/
	char	  **fc_args;			/* 包含属性名称的参数	*/
	TupleDesc	fc_tupdesc;		/* 元组描述符				*/
	HeapTuple	fc_rettuple;		/* 要返回的元组			*/
	bool		fc_isdelete;		/* 我们是在删除吗?				*/
	HeapTuple	fc_newtuple;		/* 元组的新值		*/
	HeapTuple	fc_trigtuple;		/* 元组的原始值	*/

	if (!CALLED_AS_TRIGGER(fcinfo)) /* 内部错误 */
		elog(ERROR, "lo_manage: not fired by trigger manager");

	if (!TRIGGER_FIRED_FOR_ROW(fc_trigdata->tg_event)) /* 内部错误 */
		elog(ERROR, "%s: must be fired for row",
			 fc_trigdata->tg_trigger->tgname);

	/*
	 * 从 trigdata 中获取一些值
	 */
	fc_newtuple = fc_trigdata->tg_newtuple;
	fc_trigtuple = fc_trigdata->tg_trigtuple;
	fc_tupdesc = fc_trigdata->tg_relation->rd_att;
	fc_args = fc_trigdata->tg_trigger->tgargs;

	if (fc_args == NULL)			/* 内部错误 */
		elog(ERROR, "%s: no column name provided in the trigger definition",
			 fc_trigdata->tg_trigger->tgname);

	/* 要返回给执行器的元组 */
	if (TRIGGER_FIRED_BY_UPDATE(fc_trigdata->tg_event))
		fc_rettuple = fc_newtuple;
	else
		fc_rettuple = fc_trigtuple;

	/* 我们是在删除行吗? */
	fc_isdelete = TRIGGER_FIRED_BY_DELETE(fc_trigdata->tg_event);

	/* 获取我们感兴趣的列 */
	fc_attnum = SPI_fnumber(fc_tupdesc, fc_args[0]);

	if (fc_attnum <= 0)
		elog(ERROR, "%s: column \"%s\" does not exist",
			 fc_trigdata->tg_trigger->tgname, fc_args[0]);

	/*
	 * 处理更新
	 *
	 * 在这里，如果被监控属性的值发生变化，则与原始值关联的大对象将被解除链接。
	 */
	if (fc_newtuple != NULL &&
		bms_is_member(fc_attnum - FirstLowInvalidHeapAttributeNumber, fc_trigdata->tg_updatedcols))
	{
		char	   *fc_orig = SPI_getvalue(fc_trigtuple, fc_tupdesc, fc_attnum);
		char	   *fc_newv = SPI_getvalue(fc_newtuple, fc_tupdesc, fc_attnum);

		if (fc_orig != NULL && (fc_newv == NULL || strcmp(fc_orig, fc_newv) != 0))
			DirectFunctionCall1(be_lo_unlink,
								ObjectIdGetDatum(atooid(fc_orig)));

		if (fc_newv)
			pfree(fc_newv);
		if (fc_orig)
			pfree(fc_orig);
	}

	/*
	 * 处理行的删除
	 *
	 * 在这里，我们解除与管理属性相关联的大对象的链接
	 */
	if (fc_isdelete)
	{
		char	   *fc_orig = SPI_getvalue(fc_trigtuple, fc_tupdesc, fc_attnum);

		if (fc_orig != NULL)
		{
			DirectFunctionCall1(be_lo_unlink,
								ObjectIdGetDatum(atooid(fc_orig)));

			pfree(fc_orig);
		}
	}

	return PointerGetDatum(fc_rettuple);
}
